import type { NodeConfig, NodeData, PointTuple, TextConfig } from '@logicflow/core';
import LogicFlow, { BaseNodeModel } from '@logicflow/core';
import { computed, inject, nextTick, provide, reactive, ref, shallowReactive, watch } from 'vue';
import type {
	GraphData,
	GraphModelData,
	ModelType,
	ModelerContext,
	PropertiesPanelContext,
	PropertiesPanelData,
	PropertiesPanelView,
	ViewerContext,
} from './types';
import { getWidth } from '../models/util';

function download(filename: string, text: string) {
	const element = document.createElement('a');
	element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`);
	element.setAttribute('download', filename);

	element.style.display = 'none';
	document.body.appendChild(element);

	element.click();

	document.body.removeChild(element);
}

function createViewer(modelType: ModelType): ViewerContext {
	const viewer: ViewerContext = {
		modelType,
		// export
		exportData(filename: string = modelType.name) {
			const text = JSON.stringify(viewer.lf?.getGraphRawData());
			download(filename, text ? text.toString() : '');
		},
		// 建议使用html2canvas
		exportPng(filename: string = modelType.name, backgroundColor?: string) {
			if (filename && !filename.endsWith('.png')) {
				filename += '.png';
			}
			console.log(document.styleSheets);

			console.log(viewer.lf);
			console.log(filename);
			console.log(backgroundColor);
			console.log(viewer.lf?.getSnapshotBase64('#fff'));

			viewer.lf?.getSnapshot(filename, backgroundColor);
		},

		selectNode() {
			viewer.lf?.extension.selectionSelect.openSelectionSelect();
			viewer.lf?.on('selection:selected', (data) => {
				viewer.lf?.extension.selectionSelect.closeSelectionSelect();
			});
		},
		clearGraph() {
			viewer.lf?.graphModel.clearData();
			viewer.layerList.value = [];
		},

		// miniMap
		showMiniMap: ref(false),
		toggleMiniMap() {
			const miniMap = viewer.lf?.extension.miniMap;
			const showMiniMap = viewer.showMiniMap;
			showMiniMap.value = !showMiniMap.value;
			showMiniMap.value ? miniMap.show() : miniMap.hide();
		},
		// zoom
		getCenter(): PointTuple {
			const gm = viewer.lf?.graphModel;
			const x = gm ? gm.width / 2 : 0;
			const y = gm ? gm.height / 2 : 0;
			return [x, y];
		},
		scale: reactive({
			value: 1,
			percentage: computed(() => {
				const p: number = viewer.scale.value;
				return `${Number(p * 100).toFixed()}%`;
			}),
		}),
		zoomOut() {
			viewer.lf?.zoom(false, viewer.getCenter());
		},
		zoomIn() {
			viewer.lf?.zoom(true, viewer.getCenter());
		},
		resetZoom() {
			viewer.lf?.resetZoom();
			viewer.lf?.resetTranslate();
		},
		//刷新画布
		refreshGraph() {
			viewer.lf?.render(viewer.lf?.getGraphData());
		},
		fitView(offset: number) {
			viewer.lf?.fitView(offset);
		},
		undo() {
			viewer.lf?.undo();
		},
		redo() {
			viewer.lf?.redo();
		},

		initLogicFlow(logicflowOptions: any): void {
			if (modelType.plugins) {
				if (!logicflowOptions.plugins) logicflowOptions.plugins = [];
				logicflowOptions.plugins.push(...modelType.plugins);
			}
			viewer.lf = new LogicFlow(logicflowOptions);

			if (modelType.theme) viewer.lf?.setTheme(modelType.theme);

			modelType.nodeTypes.forEach((node) => {
				viewer.lf?.register(node);
			});
			modelType.edgeTypes?.forEach((edge) => {
				viewer.lf?.register(edge);
			});
			viewer.lf?.setDefaultEdgeType(modelType.defaultEdgeType);
			viewer.lf?.setZoomMaxSize(5);

			if (modelType.init) modelType.init(viewer.lf);

			if (modelType.newData) {
				viewer.lf?.renderRawData(modelType.newData);
			} else {
				viewer.lf?.renderRawData({});
			}

			viewer.scale.value = viewer.lf?.getTransform().SCALE_X || 1;

			viewer.lf?.on('graph:transform', (data: any) => {
				if (data.type === 'zoom' || data.type === 'resetZoom') viewer.scale.value = data.transform.SCALE_X;
			});

			//创建全局菜单
			viewer.lf.extension.menu.setMenuConfig({
				nodeMenu: [
					{
						className: 'lf-menu-up custom-menu',
						text: '上移一层',
						icon: true,
						callback: (node: any) => {
							viewer.lf?.graphModel.setElementZIndex(node.id, node.zIndex + 1);
						},
					},
					{
						className: 'lf-menu-down custom-menu',
						text: '下移一层',
						icon: true,
						callback: (node: any) => {
							viewer.lf?.graphModel.setElementZIndex(node.id, node.zIndex - 1);
						},
					},
					{
						className: 'lf-menu-top custom-menu',
						text: '置顶',
						icon: true,
						callback: (node: any) => {
							viewer.lf?.graphModel.setElementZIndex(node.id, 'top');
						},
					},
					{
						className: 'lf-menu-bottom custom-menu',
						text: '置底',
						icon: true,
						callback: (node: any) => {
							viewer.lf?.graphModel.setElementZIndex(node.id, 'bottom');
						},
					},
					{
						className: 'lf-menu-delete custom-menu',
						text: '删除',
						icon: true,
						callback: (node: any) => {
							viewer.lf?.graphModel.deleteNode(node.id);
						},
					},
					{
						className: 'lf-menu-copy custom-menu',
						text: '复制',
						icon: true,
						callback(node: any) {
							viewer.lf?.graphModel.cloneNode(node.id);
						},
					},
				], // 覆盖默认的节点右键菜单
				edgeMenu: false, // 删除默认的边右键菜单
				graphMenu: [], // 覆盖默认的边右键菜单，与false表现一样
			});
			console.log('初始化面板成功');
		},
	};
	return viewer;
}

/**
 * 使用看图工具
 * 注意：对该函数返回值中的 lf 属性，请不要解构后使用
 */
export function useViewer(modelType: ModelType): ViewerContext {
	const viewer = createViewer(modelType);
	return viewer;
}

/**
 * 使用建模工具
 * 注意：对该函数返回值中的 lf 属性，请不要解构后使用
 */
export function useModeler(model: ModelType, propertiesPanelConfig: any): ModelerContext {
	const modeler = createViewer(model);

	const _ctx = shallowReactive<PropertiesPanelContext>({
		modeler,
	});

	provide('properties_panel_context', _ctx); // 提供属性面板上下文

	// propertiesPanel
	const propertiesPanel = shallowReactive<PropertiesPanelView>({
		id: '',
		name: '',
		type: '',
		collapsed: true,
	});
	const _initModeler = () => {
		_ctx.lf = modeler.lf;

		//画布渲染完成
		modeler.lf?.on('graph:rendered', () => {});
		//画布变化了
		modeler.lf?.on('history:change', ({ data }: any) => {});

		// 节点，线点击
		modeler.lf?.on('node:click,edge:click,blank:click', async ({ data }: any) => {
			if (data) {
				propertiesPanel.component = propertiesPanelConfig[data.type] || propertiesPanelConfig.default;

				propertiesPanel.type = data.type;
				propertiesPanel.collapsed = false;
				propertiesPanel.name = data.text ? data.text.value : '未定义';
				propertiesPanel.id = data.id;
				//如果为连线类型
				if (data.type.indexOf('link') >= 0) {
					let model = modeler.lf?.getEdgeModelById(data.id);
					propertiesPanel.isAnimation = model?.properties.isAnimation || false;
					propertiesPanel.strokeWidth = model?.properties.strokeWidth || 3;
					propertiesPanel.animationStroke = model?.properties.animationStroke || 'rgb(130, 179, 102)';
					propertiesPanel.lineType = model?.properties.lineType || 'True';
				}

				await nextTick(); // 为了确保先激活 Panel 再改变数据，此处 nextTick() 的位置不要随便调整
				_ctx.selectedModel = modeler.lf?.getModelById(data.id);
			} else {
				await nextTick(); // 为了确保先激活 Panel 再改变数据，此处 nextTick() 的位置不要随便调整
				_ctx.selectedModel = undefined;
				propertiesPanel.collapsed = true;
			}
		});
	};

	const _oldInit = modeler.initLogicFlow;
	modeler.initLogicFlow = (logicflowOptions: any) => {
		_oldInit(logicflowOptions);
		_initModeler();
	};
	return Object.assign(modeler, { propertiesPanel });
}

/**
 * 使用属性面板要操作的数据对象，用于扩展实现属性面板组件
 * 注意：该函数返回响应式对象，请不要解构后使用
 */
export function usePropertiesPanelData(): PropertiesPanelData {
	const ctx = inject<PropertiesPanelContext>('properties_panel_context'); // 注入属性面板上下文
	const element = reactive<PropertiesPanelData>({
		text: '',
		properties: {},
	});

	const loadData = () => {
		if (ctx?.selectedModel) {
			element.text = ctx.selectedModel.text?.value;
			element.properties = ctx.selectedModel.properties;
		} else {
			element.text = '';
			element.properties = {};
		}
	};

	watch(
		() => element.text,
		(newVal, oldVal) => {
			console.log('text changed:', oldVal, '->', newVal);
			if (oldVal !== '') {
				let width = getWidth(newVal);
				if (width < 120) {
					width = 120;
				}
				ctx?.selectedModel?.updateAttributes({
					width: width,
				});
				ctx?.selectedModel?.updateText(newVal);
			}
		}
	);

	watch(
		() => ctx?.selectedModel,
		(newVal, oldVal) => {
			loadData();
		}
	);

	loadData();
	return element;
}

export * from './types';
